package com.echo.code.arithmetic.chapter07;

import java.util.Objects;
import java.util.function.Function;
import lombok.ToString;

/**
 * Created by baiyap on 2018/8/15.
 */
@ToString
public class Optional<T> {

  private T value;

  private Optional(T value) {
    this.value = value;
  }

  public static <E> Optional<E> getInstance() {
    return getInstance(null);
  }

  public static <E> Optional<E> getInstance(E e) {
    return new Optional<>(e);
  }

  public T getValue() {
    return value;
  }

  public void setValue(T value) {
    this.value = value;
  }

  public void reset() {
    this.setValue(null);
  }

  public <E> E map(Class<E> eClass) {
    if (Objects.nonNull(value)) {
      if (Objects.equals(value.getClass(), eClass)) {
        return (E) value;
      } else {
        throw new ClassCastException(value.getClass().getName() + " cast failed");
      }
    }
    return null;
  }

  public <E> E map(Function<T, E> function) {
    Objects.requireNonNull(function);
    return function.apply(value);
  }

  public long getLong() {
    if (Objects.nonNull(value)) {
      if (value instanceof Long) {
        return (Long) value;
      }
    }
    throw new NullPointerException("null cannot cast to long value");
  }

  public long getLongOrDefault(long defaultValue) {
    if (Objects.nonNull(value)) {
      if (value instanceof Long) {
        return (Long) value;
      }
    }
    return defaultValue;
  }

  public int getInt() {
    if (Objects.nonNull(value)) {
      if (value instanceof Integer) {
        return (Integer) value;
      }
    }
    throw new NullPointerException("null cannot cast to int value");
  }

  public int getIntOrDefault(int defaultValue) {
    if (Objects.nonNull(value)) {
      if (value instanceof Integer) {
        return (Integer) value;
      }
    }
    return defaultValue;
  }

  public byte getByte() {
    if (Objects.nonNull(value)) {
      if (value instanceof Byte) {
        return (Byte) value;
      }
    }
    throw new NullPointerException("null cannot cast to byte value");
  }

  public byte getByteOrDefault(byte defaultValue) {
    if (Objects.nonNull(value)) {
      if (value instanceof Byte) {
        return (Byte) value;
      }
    }
    return defaultValue;
  }

  public double getDouble() {
    if (Objects.nonNull(value)) {
      if (value instanceof Double) {
        return (Double) value;
      }
    }
    throw new NullPointerException("null cannot cast to double value");
  }

  public double getDoubleOrDefault(double defaultValue) {
    if (Objects.nonNull(value)) {
      if (value instanceof Double) {
        return (Double) value;
      }
    }
    return defaultValue;
  }

  public float getFloat() {
    if (Objects.nonNull(value)) {
      if (value instanceof Float) {
        return (Float) value;
      }
    }
    throw new NullPointerException("null cannot cast to float value");
  }

  public float getFloatOrDefault(float defaultValue) {
    if (Objects.nonNull(value)) {
      if (value instanceof Float) {
        return (Float) value;
      }
    }
    return defaultValue;
  }

  public boolean getBoolean() {
    if (Objects.nonNull(value)) {
      if (value instanceof Boolean) {
        return (Boolean) value;
      }
    }
    throw new NullPointerException("null cannot cast to boolean value");
  }

  public boolean getFloatOrDefault(boolean defaultValue) {
    if (Objects.nonNull(value)) {
      if (value instanceof Boolean) {
        return (Boolean) value;
      }
    }
    return defaultValue;
  }
}
